/*
 * Copyright (c) 2008 VMware, Inc.
 * Copyright (c) 2009 John Pritchard, WTKX Project Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package wtkx.wtk.content;

import java.awt.Graphics2D;

import wtkx.TableView;
import wtkx.beans.BeanDictionary;
import wtkx.collections.ArrayList;
import wtkx.collections.Dictionary;
import wtkx.collections.HashMap;
import wtkx.collections.Sequence;
import wtkx.wtk.Dimensions;
import wtkx.wtk.content.TableViewCellDecorator;

/**
 * Table cell decorator that supports dynamic rendering based on the type of
 * content being rendered.
 *
 * @author tvolkert
 */
public class TableViewMultiCellDecorator
    extends Object
    implements TableView.CellDecorator
{

    /**
     * Internal style dictionary that supports no styles.
     *
     * @author tvolkert
     */
    private static class StyleDictionary 
        extends Object
        implements Dictionary<String, Object> 
    {

        StyleDictionary(){
            super();
        }

        public Object get(Object key) {
            return null;
        }

        public Object put(String key, Object value) {
            throw new UnsupportedOperationException();
        }

        public Object remove(Object key) {
            throw new UnsupportedOperationException();
        }

        public boolean containsKey(Object key) {
            return false;
        }

        public boolean isEmpty() {
            return true;
        }
    }

    /**
     * Maps the type of value being rendered (the value class) to a specific
     * cell decorator.
     *
     * @author tvolkert
     */
    public static final class DecoratorMapping {

        private Class<?> valueClass = null;

        private TableView.CellDecorator cellDecorator = null;

        private TableViewMultiCellDecorator multiCellDecorator = null;

        DecoratorMapping(){
            super();
        }

        public Class<?> getValueClass() {
            return valueClass;
        }

        public void setValueClass(Class<?> valueClass) {
            if (valueClass == null) {
                throw new IllegalArgumentException("valueClass is null.");
            }

            Class<?> previousValueClass = this.valueClass;

            if (valueClass != previousValueClass) {
                this.valueClass = valueClass;

                if (multiCellDecorator != null) {
                    multiCellDecorator.cellDecorators.remove(previousValueClass);
                    multiCellDecorator.cellDecorators.put(valueClass, cellDecorator);
                }
            }
        }

        public void setValueClass(String valueClass) {
            if (valueClass == null) {
                throw new IllegalArgumentException("valueClass is null.");
            }

            try {
                setValueClass(Class.forName(valueClass));
            } catch (ClassNotFoundException ex) {
                throw new RuntimeException(ex);
            }
        }

        public TableView.CellDecorator getCellDecorator() {
            return cellDecorator;
        }

        public void setCellDecorator(TableView.CellDecorator cellDecorator) {
            if (cellDecorator == null) {
                throw new IllegalArgumentException("cellDecorator is null.");
            }

            TableView.CellDecorator previousCellDecorator = this.cellDecorator;

            if (cellDecorator != previousCellDecorator) {
                this.cellDecorator = cellDecorator;

                if (multiCellDecorator != null) {
                    multiCellDecorator.cellDecorators.put(valueClass, cellDecorator);
                }
            }
        }

        private void setMultiCellDecorator(TableViewMultiCellDecorator multiCellDecorator) {
            this.multiCellDecorator = multiCellDecorator;
        }
    }

    /**
     * Provides a sequence hook into this decorator's mappings, thus enabling
     * developers to define their multi-cell decorator in WTKX.
     *
     * @author tvolkert
     */
    private class DecoratorMappingSequence 
        implements Sequence<DecoratorMapping> 
    {
        private ArrayList<DecoratorMapping> mappings = new ArrayList<DecoratorMapping>();

        DecoratorMappingSequence(){
            super();
        }

        public int add(DecoratorMapping item) {
            int index = mappings.getLength();
            insert(item, index);
            return index;
        }

        public void insert(DecoratorMapping item, int index) {
            if (item == null) {
                throw new IllegalArgumentException("item is null.");
            }

            Class<?> valueClass = item.getValueClass();

            if (cellDecorators.containsKey(valueClass)) {
                throw new IllegalArgumentException("Duplicate value class mapping: " +
                    (valueClass == null ? "null" : valueClass.getName()));
            }

            mappings.insert(item, index);

            TableView.CellDecorator cellDecorator = item.getCellDecorator();
            cellDecorators.put(valueClass, cellDecorator);

            item.setMultiCellDecorator(TableViewMultiCellDecorator.this);
        }

        public DecoratorMapping update(int index, DecoratorMapping item) {
            if (item == null) {
                throw new IllegalArgumentException("item is null.");
            }

            if (index >= getLength()) {
                throw new IndexOutOfBoundsException();
            }

            DecoratorMapping previousItem = mappings.get(index);

            if (item != previousItem) {
                Class<?> valueClass = item.getValueClass();
                Class<?> previousValueClass = previousItem.getValueClass();

                if (cellDecorators.containsKey(valueClass)
                    && valueClass != previousValueClass) {
                    throw new IllegalArgumentException("Duplicate value class mapping: " +
                        valueClass.getName());
                }

                mappings.update(index, item);

                TableView.CellDecorator cellDecorator = item.getCellDecorator();
                cellDecorators.remove(previousValueClass);
                cellDecorators.put(valueClass, cellDecorator);

                previousItem.setMultiCellDecorator(null);
                item.setMultiCellDecorator(TableViewMultiCellDecorator.this);
            }

            return previousItem;
        }

        public int remove(DecoratorMapping item) {
            int index = mappings.indexOf(item);

            if (index >= 0) {
                remove(index, 1);
            }

            return index;
        }

        public Sequence<DecoratorMapping> remove(int index, int count) {
            Sequence<DecoratorMapping> removed = mappings.remove(index, count);

            for (int i = 0, n = removed.getLength(); i < n; i++) {
                DecoratorMapping item = removed.get(i);
                Class<?> valueClass = item.getValueClass();

                cellDecorators.remove(valueClass);
                item.setMultiCellDecorator(null);
            }

            return removed;
        }

        public DecoratorMapping get(int index) {
            return mappings.get(index);
        }

        public int indexOf(DecoratorMapping item) {
            return mappings.indexOf(item);
        }

        public int getLength() {
            return mappings.getLength();
        }
    }

    private static final StyleDictionary STYLES = new StyleDictionary();


    private int width;
    private int height;

    private HashMap<Class<?>, TableView.CellDecorator> cellDecorators =
        new HashMap<Class<?>, TableView.CellDecorator>();

    private TableView.CellDecorator defaultDecorator = new TableViewCellDecorator();
    private TableView.CellDecorator currentDecorator = defaultDecorator;

    private DecoratorMappingSequence decoratorMappings = new DecoratorMappingSequence();


    public TableViewMultiCellDecorator(){
        super();
    }


    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    public void paint(Graphics2D graphics) {
        currentDecorator.paint(graphics);
    }

    public void setSize(int width, int height) {
        this.width = width;
        this.height = height;

        currentDecorator.setSize(width, height);
    }

    public int getPreferredWidth(int height) {
        return currentDecorator.getPreferredWidth(height);
    }

    public int getPreferredHeight(int width) {
        // Our preferred height is the maximum of all our possible decorators'
        // preferred height
        int preferredHeight = defaultDecorator.getPreferredHeight(width);

        for (Class<?> key : cellDecorators) {
            TableView.CellDecorator decorator = cellDecorators.get(key);
            preferredHeight = Math.max(preferredHeight,
                decorator.getPreferredHeight(width));
        }

        return preferredHeight;
    }

    public Dimensions getPreferredSize() {
        return new Dimensions(getPreferredWidth(-1), getPreferredHeight(-1));
    }

    public Dictionary<String, Object> getStyles() {
        return STYLES;
    }

    @SuppressWarnings("unchecked")
    public void render(Object value, TableView tableView, TableView.Column column,
                       boolean rowSelected, boolean rowHighlighted, boolean rowDisabled)
    {
        Object cellData = null;

        // Get the row and cell data
        String columnName = column.getName();
        if (columnName != null) {
            Dictionary<String, Object> rowData;
            if (value instanceof Dictionary<?, ?>) {
            	rowData = (Dictionary<String, Object>)value;
            } else {
            	rowData = new BeanDictionary(value);
            }

            cellData = rowData.get(columnName);
        }

        Class<?> valueClass = (cellData == null ? null : cellData.getClass());
        TableView.CellDecorator cellDecorator = cellDecorators.get(valueClass);

        if (cellDecorator == null) {
            cellDecorator = defaultDecorator;
        }

        if (cellDecorator != currentDecorator) {
            currentDecorator = cellDecorator;
            cellDecorator.setSize(width, height);
        }

        cellDecorator.render(value, tableView, column, rowSelected, rowHighlighted, rowDisabled);
    }

    public TableView.CellDecorator getDefaultDecorator() {
        return defaultDecorator;
    }

    public void setDefaultDecorator(TableView.CellDecorator defaultDecorator) {
        if (defaultDecorator == null) {
            throw new IllegalArgumentException("defaultDecorator is null.");
        }

        this.defaultDecorator = defaultDecorator;
    }

    public Sequence<DecoratorMapping> getDecoratorMappings() {
        return decoratorMappings;
    }
}
